package edu.ilstu.mind.ise.ccp.config;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

/**
 * Utility class to instantiate an object defined in a YAML configuration. This
 * class assumes there is a default constructor when no properties are
 * specified, or there is a constructor that takes Map<String,Object> when
 * properties are specified. <BR>
 * See {@link ConfigParserUtil} for utility functions for parsing the
 * Map<String,Object>
 * 
 * @author Eric
 * 
 */
public class YamlObjectInstantiator {
        private final String classKey;
        private final String propertiesKey;

        /**
         * 
         * @param classKey
         *            The YAML key who's value defines the class to be instantiated
         * @param propertiesKey
         *            The YAML key who's value represents a Map<String,Object> to be
         *            used in the constructor of the class to be instantiated
         */
        public YamlObjectInstantiator(String classKey, String propertiesKey) {
                this.classKey = classKey;
                this.propertiesKey = propertiesKey;
        }

        /**
         * Instantiate all top level elements in the objectsConfigMap
         * 
         * @param objectsConfigMap
         * @return Map<String Object Name, Instantiated Object>
         * @throws NoSuchMethodException
         * @throws ClassNotFoundException
         * @throws InstantiationException
         * @throws IllegalAccessException
         * @throws InvocationTargetException
         */
        @SuppressWarnings("unchecked")
        public Map<String, ?> instantiateObjects(Map<String, Object> objectsConfigMap) throws NoSuchMethodException, ClassNotFoundException,
                        InstantiationException, IllegalAccessException, InvocationTargetException {
                Map<String, Object> map = new HashMap<String, Object>();
                for (Map.Entry<String, ?> entry : objectsConfigMap.entrySet()) {
                        String name = entry.getKey();
                        Map<String, Object> cfg = (Map<String, Object>) entry.getValue();
                        Object receiver = instantiateObject(cfg);
                        map.put(name, receiver);
                }
                return map;
        }

        /**
         * Instantiate an object. Uses classKey and propertiesKey passed in the
         * constructor
         * 
         * @param objectConfig
         *            Config for the object
         * @return The instantiated object
         * @throws NoSuchMethodException
         * @throws ClassNotFoundException
         * @throws InstantiationException
         * @throws IllegalAccessException
         * @throws InvocationTargetException
         */
        @SuppressWarnings("unchecked")
        public Object instantiateObject(Map<String, Object> objectConfig) throws NoSuchMethodException, ClassNotFoundException,
                        InstantiationException, IllegalAccessException, InvocationTargetException {
                String clazz = (String) objectConfig.get(classKey);
                Map<String, Object> properties = (Map<String, Object>) objectConfig.get(propertiesKey);
                Object obj = instantiateObject(clazz, properties);
                return obj;
        }

        /**
         * Instantiate an object given the class name and the properties
         * 
         * @param className
         *            Class to instantiate
         * @param properties
         *            Properties to be passed into the constructor (null to use
         *            default constructor)
         * @return The instantiated object
         * @throws NoSuchMethodException
         * @throws ClassNotFoundException
         * @throws InstantiationException
         * @throws IllegalAccessException
         * @throws InvocationTargetException
         */
        public static Object instantiateObject(String className, Map<String, Object> properties) throws NoSuchMethodException,
                        ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException {
                if (properties == null) {
                        return Class.forName(className).newInstance();
                } else {
                        Constructor<?> constructor = Class.forName(className).getConstructor(Map.class);
                        return constructor.newInstance(properties);
                }
        }
}
